﻿#region

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Radovici.SharedLibrary;
using YouTubePlaylist.MusicVideoService;

#endregion

namespace YouTubePlaylist.Models
{
    public class PlaylistsModel : Notifiable, ISelector<PlaylistModel>
    {
        #region Private fields

        private readonly DataLayer _model;
        private readonly ObservableCollection<PlaylistModel> _playlistModels = new ObservableCollection<PlaylistModel>();
        private readonly ObservableCollection<VideoPlaylist> _playlists;

        private PlaylistModel _curPlaylist;
        private PlaylistModel _nextPlaylist;
        private PlaylistModel _prevPlaylist;

        private string _selectedOption;
        private VideoPlaylist _selectedPlaylist;
        private string _title = string.Empty;

        #endregion

        public PlaylistsModel(ObservableCollection<VideoPlaylist> playlists)
        {
            _model = DataLayer.Singleton;
            _playlists = playlists;
            _playlists.CollectionChanged += OnPlaylistsChanged;
            _playlistModels.CollectionChanged += OnPlaylistModelsChanged;
            AddPlaylists(playlists);
        }

        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                Notify("Title");
            }
        }

        public DataLayer Model
        {
            get { return _model; }
        }

        public ObservableCollection<VideoPlaylist> Playlists
        {
            get { return _playlists; }
        }

        public ObservableCollection<PlaylistModel> PlaylistModels
        {
            get { return _playlistModels; }
        }

        public bool CanUserAddNew
        {
            get { return _model.CanUserAddNew(Playlists); }
        }

        public bool CanUserAdd
        {
            get { return _model.CanUserAdd(Playlists); }
        }

        public bool CanUserDelete
        {
            get { return _model.CanUserDelete(Playlists); }
        }

        public bool CanUserEdit
        {
            get { return _model.CanUserEdit(Playlists); }
        }

        public bool HasPlaylists
        {
            get { return _playlistModels.Count > 0; }
        }

        public VideoPlaylist SelectedPlaylist
        {
            get { return _selectedPlaylist; }
            set
            {
                _selectedPlaylist = value;
                Notify("SelectedPlaylist");
                Notify("IsPlaylistSelected");
            }
        }

        public bool IsPlaylistSelected
        {
            get { return _selectedPlaylist != null; }
        }

        public string SelectedOption
        {
            get { return _selectedOption; }
            set
            {
                _selectedOption = value;
                Notify("SelectedOption");
                Notify("SelectedOptionHelp");
            }
        }

        public string SelectedOptionHelp
        {
            get
            {
                string help = null;
                switch (_selectedOption)
                {
                    case "mail":
                        help = "Mail this playlist to a friend.";
                        break;
                    case "link":
                        help = "Copy this playlist's link.";
                        break;
                    case "copy":
                        help = "Copy this playlist.";
                        break;
                    case "edit":
                        help = "Edit this playlist.";
                        break;
                    case "delete":
                        help = "Delete this playlist from your playlists.";
                        break;
                    case "categorize":
                        help = "Copy and categorize this playlist.";
                        break;
                }
                return help;
            }
        }

        #region ISelector<PlaylistModel> Members

        public PlaylistModel Current
        {
            get
            {
                //if (PlaylistsViewModels.Count == 0) return new PlaylistModel(new VideoPlaylist {Tracks = new ObservableCollection<VideoTrack>()});
                //if (!PlaylistsViewModels.Contains(_curPlaylist))
                //{
                //    Current = Next;
                //}
                return _curPlaylist;
                //return new PlaylistModel(new VideoPlaylist() { Tracks = new ObservableCollection<VideoTrack>() });
            }
            set
            {
                _prevPlaylist = _curPlaylist;
                _curPlaylist = value;
                _nextPlaylist = GetNextPlaylist();
                NotifyAll();
            }
        }

        public bool HasPrevious
        {
            get { return Previous != null; }
        }

        public PlaylistModel Previous
        {
            get { return _prevPlaylist; }
        }

        public bool HasNext
        {
            get { return Next != null; }
        }

        public PlaylistModel Next
        {
            get
            {
                //if (!PlaylistsViewModels.Contains(_nextPlaylist))
                //{
                //    _nextPlaylist = GetNextPlaylist();
                //}
                return _nextPlaylist;
            }
        }

        #endregion

        #region Private methods

        private PlaylistModel GetNextPlaylist()
        {
            PlaylistModel next = null;
            if (HasPlaylists)
            {
                int curIndex = _playlistModels.IndexOf(_curPlaylist);
                next = _playlistModels[(curIndex + 1)%_playlistModels.Count];
            }
            return next;
        }

        private void OnPlaylistsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                AddPlaylists(e.NewItems.Cast<VideoPlaylist>());
            }
            if (e.OldItems != null)
            {
                foreach (VideoPlaylist playlist in e.OldItems)
                {
                    int pId = playlist.Id;
                    PlaylistModel pVm = _playlistModels.SingleOrDefault(p => p.Playlist.Id == pId);
                    pVm.PropertyChanged -= OnPlaylistModelChanged; //NOTE: Exception thrown if PlaylistModel not found (as intended).
                    _playlistModels.Remove(pVm);
                }
            }
        }

        private void OnPlaylistModelChanged(object sender, PropertyChangedEventArgs e)
        {
            var pVm = sender as PlaylistModel;
            if (pVm != null)
            {
                switch (e.PropertyName)
                {
                    case "Current":
                        if (pVm.IsPlaylistFinished)
                        {
                            Current = Next;
                        }
                        break;
                }
            }
        }

        private void AddPlaylists(IEnumerable<VideoPlaylist> playlists)
        {
            if (playlists != null && playlists.Count() > 0)
            {
                foreach (VideoPlaylist playlist in playlists)
                {
                    var pVm = new PlaylistModel(playlist);
                    pVm.PropertyChanged += OnPlaylistModelChanged;
                    _playlistModels.Add(pVm);
                }
            }
        }

        private void OnPlaylistModelsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (Current == null && PlaylistModels.Count > 0)
            {
                Current = PlaylistModels[0];
            }
            NotifyAll();
        }

        public void NotifyAll() //TODO: Make Notifiable function making use of reflection (public properties, possibly attributed).
        {
            Notify("SelectedOption");
            Notify("SelectedOptionHelp");
            Notify("SelectedPlaylist");
            Notify("IsPlaylistSelected");

            Notify("Title");
            Notify("CanUserEdit");
            Notify("Playlists");
            Notify("HasPlaylists");
            Notify("Current");
            Notify("HasPrevious");
            Notify("Previous");
            Notify("HasNext");
            Notify("Next");
        }

        #endregion

        public void Add(PlaylistModel playlist)
        {
            _model.AddPlaylist(playlist.Playlist, _playlists);
        }

        public void Add(VideoPlaylist playlist)
        {
            _model.AddPlaylist(playlist, _playlists);
        }

        public void CategorizePlaylist(VideoPlaylist playlist, string category)
        {
            if (string.IsNullOrEmpty(category))
                throw new ArgumentException("Missing category; can't categorize playlist.");
            string catSuffix = string.Format(" ({0})", category);
            if (playlist.Name.EndsWith(catSuffix))
                throw new ArgumentException("Copy playlist; this playlist is already similarly categorized.");
            var p = new VideoPlaylist();
            p.Name = playlist.Name + catSuffix;
            p.Tracks = new ObservableCollection<VideoTrack>();
            foreach (VideoTrack track in playlist.Tracks)
            {
                p.Tracks.Add(new VideoTrack {Artist = track.Artist, Name = track.Name, Category = category});
            }
            _model.AddPlaylist(p);
        }

        public void DeletePlaylist(VideoPlaylist playlist)
        {
            _model.DeletePlaylist(playlist, Playlists);
        }

        public void CopyPlaylist(VideoPlaylist playlist)
        {
            _model.CopyPlaylist(playlist);
        }

        public void SavePlaylist(VideoPlaylist playlist)
        {
            _model.AddPlaylist(playlist.GetEmptyCopy());
        }
    }
}